#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define DELIM " \t"
#define EXIT_CODE 44

#define NONE -1
#define IN_REDIR 0
#define OUT_REDIR 1
#define APPEND_REDIR 2


extern char** environ;
char _command_line[LINE_SIZE];
char *_argv[ARGC_SIZE];
char _pwd[LINE_SIZE];
char _env_var[LINE_SIZE]; // 自定义环境变量添加表
int _quit=0;
int _last_code=0;

char* redir_filename;
int redir_flag=0;

const char* get_username()
{
	return getenv("USER");
}

const char* get_hostname()
{
	return getenv("HOSTNAME");
}

const char* get_pwd()
{
	getcwd(_pwd,sizeof(_pwd)-1);
}


void check_redir(char cmd[])
{
	char* pos=cmd;
	while(*pos)
	{
		if(*pos=='>')
		{
			if(*(pos+1)=='>') // 此处应该判断是否越界然后报错
			{
				*pos++='\0';
				*pos++='\0';
				while(*pos==' ')
					++pos;
				redir_filename=pos;
				redir_flag=APPEND_REDIR;
				break;
			}
			else
			{
				*pos++='\0';
				while(*pos==' ')
					++pos;
				redir_filename=pos;
				redir_flag=OUT_REDIR;
				break;
			}
		}
		else if(*pos=='<')
		{
			*pos++='\0';
			while(*pos==' ')
				++pos;
			redir_filename=pos;
			redir_flag=IN_REDIR;
			break;
		}
		++pos;
	}
}

void run(char line[],int sz)
{
	get_pwd();
	printf(LEFT "%s@%s %s " RIGHT LABLE,get_username(),get_hostname(),_pwd);
	char* s=fgets(line,sz,stdin);
	assert(s);
	(void)s;
	line[strlen(line)-1]='\0';
	check_redir(line);
}

int split_string(char command[],char *argv[])
{
	int i=0;
	argv[i++]=strtok(command,DELIM);
	while(argv[i++]=strtok(NULL,DELIM));

	return i-1;
}

void normal_run_command(char* argv[])
{
	pid_t id=fork();
	if(id<0)
	{
		perror("fork");
		return;
	}
	else if(id==0)
	{
		int fd=0;
		if(redir_flag==IN_REDIR)
		{
			fd=open(redir_filename,O_RDONLY,0666);
			dup2(fd,0); // fork()子进程不会影响父进程,进程之间互不影响，而每个进程启动都会默认打开012的文件操作符，此处修改的是子进程的fd
		}
		else if(redir_flag==OUT_REDIR)
		{
			fd=open(redir_filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
			dup2(fd,1);
		}
		else if(redir_flag==APPEND_REDIR)
		{
			fd=open(redir_filename,O_CREAT | O_WRONLY |O_APPEND,0666);
			dup2(fd,1);
		}

		execvpe(argv[0],argv,environ); // 子进程替换
		exit(EXIT_CODE);
	}
	else // 回收子进程
	{
		int status=0;
		pid_t pid=waitpid(id,&status,0);
		if(pid==id)
		{
			_last_code=WEXITSTATUS(status);
		}
	}

}


int build_command(char* argv[],int argc)
{
	if(argc==2 && strcmp(argv[0],"cd")==0)
	{
		chdir(argv[1]); // 更改自身路径
		get_pwd(); 
		sprintf(getenv("PWD"),"%s",_pwd); // 更改环境变量PWD
		return 1;
	}
	else if(argc==2 && strcmp(argv[0],"export")==0)
	{
		strcpy(_env_var,argv[1]);
		putenv(_env_var);
		return 1;
	}
	else if(argc==2 && strcmp(argv[0],"echo")==0)
	{
		if(strcmp(argv[1],"$?")==0)
		{
			printf("%d\n",_last_code);
		}
		else if(*argv[1]=='$') // 是环境变量
		{
			// getenv()不是把变量添加到环境变量，而是在环境变量表保存变量的地址，而argv的内容是会变化的所以需要单独开一个区域存储需要添加的变量
			char* val=getenv(argv[1]+1); 
			if(val)
				printf("%s\n",val);
		}
		else // 不是环境变量
			printf("%s\n",argv[1]);
		
		return 1;
	}

	if(strcmp(argv[0],"ls")==0)
	{
		argv[argc++]="--color";
		argv[argc]=NULL;
	}	
	return 0;
}

int main()
{
	while(!_quit)
	{
		redir_filename=NULL;
		redir_flag=NONE;
		run(_command_line,sizeof(_command_line)); // run	
		int argc=split_string(_command_line,_argv); // 分割命令
		if(argc==0) 
			continue;
		// 创建子进程执行命令
		int n=build_command(_argv,argc); // 判断内建命令
		
		if(n==0)
			normal_run_command(_argv); // 普通命令

	}

	return 0;
}
